#聊天室的服务器，客户机断开了，服务器不要断开
#且支持多人聊天，有群发功能

import socket
import select #select里面的epoll只能在Linux下使用，Windows没有这个接口
import sys

def tcp_server():
    # SOCK_STREAM是TCP里的socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    addr=('192.168.124.132',2000)
    s.bind(addr)#这里没激活端口
    s.listen(128)#这里端口才激活
    epoll=select.epoll()#创建一个epoll对象，用来监听，EPOLLIN是可读事件
    epoll.register(s.fileno(),select.EPOLLIN)  #  有客户端连接，就会触发s。 fileno()也是个缓冲区其实，不是accept放这里了 他可能会卡住
    epoll.register(sys.stdin.fileno(),select.EPOLLIN)  #  stdin是标准输入缓冲区
    client_list=[]
    while True:
        # 谁的缓冲区有数据，epoll监听到了，就写到events,events是列表里边存的是元组，（fd,事件）
        #缓冲区没有数据的不会被写进列表
        events=epoll.poll(-1)  #-1是一直等待直到文件句柄（fd)发生变化
        for fd,event in events:#fd 是 fileno（文件标识符）
            if fd==s.fileno():  #有客户端连接，就连上，得到客户端new_client，放入列表，注册它，这样下面的accept就不会卡住了
                # 接受客户端的连接，128里出来一个 它和客户端通信， accept以后,服务器就知道发给谁了
                new_client, client_addr = s.accept()
                print(client_addr)
                client_list.append(new_client)
                epoll.register(new_client.fileno(),select.EPOLLIN)
            else:#文件描述符标识的是标准输入缓冲区有数据了，就要群发
                remove_client=None
                #群发功能，遍历所有的客户端对象
                for client in client_list:
                    if client.fileno()==fd:   #如果这个客户端是标识的有输入数据的那个
                        data=client.recv(1000)#那么就要接收这个客户端发来的数据（input在客户端那个代码里）
                        if data:#如果收到了数据，就群发
                            for other_client in client_list:
                                if other_client is client:
                                    pass
                                else:
                                    other_client.send(data)
                        else:#没有数据就说明断开了，记录一下
                            remove_client=client
                if remove_client:#如果有人断开了，就移除
                    client_list.remove(remove_client)
                    epoll.unregister(remove_client.fileno())
                    remove_client.close()
    s.close()

if __name__ == '__main__':
    tcp_server()



